package com.aiks.dsyncpluginspringbootautoconfigure.plugin.factory;

import com.aiks.dsyncpluginspringbootautoconfigure.BusinessException;
import com.aiks.dsyncpluginspringbootautoconfigure.enums.ModuleTypeEnum;
import com.aiks.dsyncpluginspringbootautoconfigure.plugin.Plugin;
import com.aiks.dsyncpluginspringbootautoconfigure.plugin.PluginChain;
import com.aiks.dsyncpluginspringbootautoconfigure.plugin.module.DeptModule;
import com.aiks.dsyncpluginspringbootautoconfigure.plugin.module.GradeModule;
import com.aiks.dsyncpluginspringbootautoconfigure.plugin.module.PositionCategoryModule;
import com.aiks.dsyncpluginspringbootautoconfigure.plugin.module.PositionModule;
import com.aiks.dsyncpluginspringbootautoconfigure.plugin.module.UserModule;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.concurrent.ConcurrentHashMap;

/**
 * PluginFactory.
 *
 * @author <a href="mailto:yuanz@yxt.com">yuanz</a>
 * @date 2022-06-28 11:13
 * @since 1.0.0
 **/
public class PluginFactory {

    public static final PluginFactory INSTANCE = new PluginFactory();

    private static final ConcurrentHashMap<String, Plugin> USER_PLUGIN_MAP = new ConcurrentHashMap();

    private static final ConcurrentHashMap<String, Plugin> DEPT_PLUGIN_MAP = new ConcurrentHashMap();

    private static final ConcurrentHashMap<String, Plugin> POSITION_PLUGIN_MAP = new ConcurrentHashMap();

    private static final ConcurrentHashMap<String, Plugin> GRADE_PLUGIN_MAP = new ConcurrentHashMap();

    private static final ConcurrentHashMap<String, Plugin> POSITION_CATEGORY_PLUGIN_MAP = new ConcurrentHashMap();

    private static final ConcurrentHashMap<String, PluginChain> PLUGIN_CHAIN_MAP = new ConcurrentHashMap();

    public void init(Collection<Plugin> plugins) {
        for (Plugin plugin : plugins) {
            distributionPlugin(plugin);
        }
    }

    static {
        Iterator<Plugin> iterator = ServiceLoader.load(Plugin.class).iterator();
        while (iterator.hasNext()) {
            Plugin plugin = iterator.next();
            distributionPlugin(plugin);
        }
    }

    private static void distributionPlugin(Plugin plugin) {
        if (plugin instanceof UserModule) {
            USER_PLUGIN_MAP.putIfAbsent(plugin.getPluginEnum().getPluginName(), plugin);
        } else if (plugin instanceof DeptModule) {
            DEPT_PLUGIN_MAP.putIfAbsent(plugin.getPluginEnum().getPluginName(), plugin);
        } else if (plugin instanceof PositionModule) {
            POSITION_PLUGIN_MAP.putIfAbsent(plugin.getPluginEnum().getPluginName(), plugin);
        } else if (plugin instanceof GradeModule) {
            GRADE_PLUGIN_MAP.putIfAbsent(plugin.getPluginEnum().getPluginName(), plugin);
        } else if (plugin instanceof PositionCategoryModule) {
            POSITION_CATEGORY_PLUGIN_MAP.putIfAbsent(plugin.getPluginEnum().getPluginName(), plugin);
        } else {
            throw new BusinessException("");
        }
    }

    /**
     * getPluginMap.
     *
     * @param moduleTypeEnum moduleTypeEnum
     * @return Map
     */
    public Map<String, Plugin> getPluginMap(final ModuleTypeEnum moduleTypeEnum) {
        Map<String, Plugin> map = new HashMap<>();
        switch (moduleTypeEnum) {
            case user:
                map = Collections.unmodifiableMap(USER_PLUGIN_MAP);
                break;
            case dept:
                map = Collections.unmodifiableMap(DEPT_PLUGIN_MAP);
                break;
            case position:
                map = Collections.unmodifiableMap(POSITION_PLUGIN_MAP);
                break;
            case grade:
                map = Collections.unmodifiableMap(GRADE_PLUGIN_MAP);
                break;
            case position_category:
                map = Collections.unmodifiableMap(POSITION_CATEGORY_PLUGIN_MAP);
                break;
            default:
                break;
        }
        return map;
    }

    /**
     * getPluginChain.
     *
     * @param moduleTypeEnum moduleTypeEnum
     * @return PluginChain
     */
    public PluginChain getPluginChain(final ModuleTypeEnum moduleTypeEnum) {
        PluginChain pluginChain = PLUGIN_CHAIN_MAP.get(moduleTypeEnum.getValue());
        if (null == pluginChain) {
            synchronized (this) {
                if (null == pluginChain) {
                    return PLUGIN_CHAIN_MAP.computeIfAbsent(moduleTypeEnum.getValue(), t -> new PluginChain(moduleTypeEnum));
                }
            }
            return PLUGIN_CHAIN_MAP.get(moduleTypeEnum.getValue());
        } else {
            return pluginChain;
        }
    }
}
